package pers.zifeng.distributed.lock.redis.event;

import jodd.util.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import pers.zifeng.distributed.lock.common.MysqlService;
import pers.zifeng.distributed.lock.redis.service.JedisLockService;
import redis.clients.jedis.JedisPubSub;

import java.lang.management.ManagementFactory;
import java.util.UUID;

/**
 * @author: zf
 * @date: 2021/06/23 09:40:56
 * @version: 1.0.0
 * @description: 基于jedis实现的分布式锁
 */
@Slf4j
public class JedisLock {
    private static final Object lock = new Object();
    // 当前线程号
    private static final String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
    // 锁名称
    private static final String LOCK_KEY = "buyMask";
    // channel名称
    private static final String CHANNEL_NAME = "buyMask-channel";
    // 接收消息处理
    private static final MyJedisPubSub myJedisPubSub = new MyJedisPubSub();//处理接收消息

    public static void buyMaskWithLock() {
        boolean getLock, releaseLock;
        String clientUUID;
        try {
            while (true) {
                // 客户端uuid标识
                clientUUID = UUID.randomUUID().toString();
                log.info("【当前线程:" + pid + "】 尝试获取锁！");
                getLock = JedisLockService.tryAndGetDistributedLock(LOCK_KEY, clientUUID, 10 * 3000);
                if (getLock) {
                    log.info("【当前线程:" + pid + "】 获取锁成功！");
                    if (!MysqlService.buyMask()) {
                        break;
                    }
                    releaseLock = JedisLockService.releaseDistributedLock(LOCK_KEY, clientUUID);
                    if (releaseLock) {
                        log.info("【当前线程:" + pid + "】 释放锁成功！");
                        JedisLockService.publish(CHANNEL_NAME, LOCK_KEY + " release!");
                        ThreadUtil.sleep(10 * 1000);
                    } else {
                        throw new Exception("【当前线程:" + pid + "】 释放锁失败，当前锁持有者已改变！");
                    }
                } else {
                    log.info("【当前线程:" + pid + "】 获取锁失败！阻塞后重新获取！");
                    synchronized (lock) {
                        new Thread(() -> JedisLockService.subscribe(myJedisPubSub, CHANNEL_NAME)).start();
                        lock.wait(20 * 1000);
                        myJedisPubSub.unsubscribe(CHANNEL_NAME);
                    }
                }
            }
        } catch (Exception e) {
            log.error("抢购口罩失败！", e);
        } finally {
            JedisLockService.close();
        }
    }

    public static void main(String[] args) {
        buyMaskWithLock();
    }

    /**
     * 继承JedisPubSub，重写接收消息的方法
     */
    static class MyJedisPubSub extends JedisPubSub {
        @Override
        public void onMessage(String channel, String message) {
            log.info("【当前线程:" + pid + "】 接收到消息:" + message);
            if ((LOCK_KEY + " release!").equals(message)) {
                synchronized (lock) {
                    lock.notify();
                }
            }
        }

    }

}
